<!DOCTYPE HTML>
<!--
	Dimension by HTML5 UP
	html5up.net | @ajlkn
	Free for personal and commercial use under the CCA 3.0 license (html5up.net/license)
-->
<html>
 <head>
  <title>
   Dimension by HTML5 UP
  </title>
  <!-- <meta charset="utf-8" /> -->
  <!-- <meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no" /> -->
  <meta charset="utf-8"/>
  <meta content="width=device-width,initial-scale=1.0" name="viewport"/>
  <link href="../../assets/css/article.css" rel="stylesheet"/>
  <link href="https://cdn.bootcss.com/highlight.js/9.15.8/styles/github.min.css" rel="stylesheet"/>
  <noscript>
   <link href="../../assets/css/noscript.css" rel="stylesheet"/>
  </noscript>
 </head>
 <body>
  <div id="app">
  </div>
  <!-- built files will be auto injected -->
 </body>
 <body class="is-preload">
  <!-- Wrapper -->
  <div id="wrapper">
   <!-- Main -->
   <div id="main">
    <article id="article">
     <h1 id="nginx">
      Nginx跨域解决配置示例
     </h1>
     <hr/>
     <p>
      这是我参与2022首次更文挑战的第25天，活动详情查看：
      <a href="https://juejin.cn/post/7052884569032392740">
       2022首次更文挑战
      </a>
     </p>
     <h2 id="_1">
      简介
     </h2>
     <p>
      在日常学习和工作开发中，需要请求两个不同配置的请求经常存在，本文介绍如果还使用Nginx配置解决其跨域问题
     </p>
     <h2 id="_2">
      相关理论
     </h2>
     <p>
      首先需要了解什么是跨域，下面的两个文章说的很好，请仔细阅读后，然后自己去动手尝试配置，将有深刻的体会
     </p>
     <ul>
      <li>
       <a href="https://segmentfault.com/a/1190000015597029">
        不要再问我跨域的问题了
       </a>
      </li>
      <li>
       <a href="https://segmentfault.com/a/1190000022506474">
        【译】3种解决CORS错误的方式与Access-Control-Allow-Origin的作用原理
       </a>
      </li>
      <li>
       <a href="https://blog.csdn.net/ouyida3/article/details/86771683">
        Nginx实战（九）跨域配置（解决CORS报错）
       </a>
      </li>
      <li>
       <a href="https://yijiebuyi.com/blog/ba792f47a3e713e3c63082587a6d8675.html">
        nginx代理跨域配置add_header Access-Control-Allow-Origin 不生效的解决方法
       </a>
      </li>
     </ul>
     <p>
      总结来说：
     </p>
     <ul>
      <li>
       跨域是浏览器的安全策略造成的，但其也是必要的，不能为了方便而放弃安全性
      </li>
      <li>
       跨域是不同源的请求导致的：IP、域名、端口等不同都会造成跨域
      </li>
      <li>
       跨域的判断是由请求头和响应头的相关字段进行判断的，这个是设置的基础
      </li>
     </ul>
     <p>
      那解决方法目前看来有三个：
     </p>
     <ul>
      <li>
       前端层面自己解决：前端请求时自己进行代理
      </li>
      <li>
       网关层面进行解决：在nginx、kong同统一网关中进行配置解决
      </li>
      <li>
       服务后台解决：在Go、Java Web中进行配置解决，经典的Cors配置
      </li>
     </ul>
     <p>
      但注意的是，有时候是只能使用一样跨域解决方式的，最终的效果是需要保证最后前端收到的请求头符合规范,特别是下面这个头：
     </p>
     <p>
      Access-Control-Allow-Origin: *
     </p>
     <p>
      如果返回的响应头里面少了会跨域，但多了，比如返回了两个相同的，也会跨域
     </p>
     <p>
      注：在实际开发中，如果发生跨域，排查的第一步就是看看响应头里面是否返回了正确的数据，这样就能精确的进行下一步的解决操作
     </p>
     <h2 id="_3">
      配置示例
     </h2>
     <h3 id="_4">
      场景描述
     </h3>
     <p>
      场景如下：
     </p>
     <p>
      前端需要请求两个服务：
     </p>
     <ul>
      <li>
       http://www.service1.com/getxxx
      </li>
      <li>
       http://www.service2.com/getxxx
      </li>
     </ul>
     <p>
      而页面的访问链接是：http://www.web.com
     </p>
     <p>
      如果我们不进行任何配置，通过浏览器访问页面，两个服务的请求都会报：Cors Err
     </p>
     <p>
      注：这里就没有示例代码了，自己可以用vue简单写写，然后访问
     </p>
     <h3 id="nginx_1">
      Nginx配置
     </h3>
     <p>
      下面展示如果通过配置Nginx解决跨域问题：
     </p>
     <p>
      配置两个服务，监听在相同的端口，服务名不同而已，在转发配置中加上跨域配置，示例文件如下：
     </p>
     <div class="codehilite">
      <pre><span></span><code><span class="c1"># For more information on configuration, see:</span>
<span class="c1">#   * Official English Documentation: http://nginx.org/en/docs/</span>
<span class="c1">#   * Official Russian Documentation: http://nginx.org/ru/docs/</span>

<span class="k">user</span> <span class="s">nginx</span><span class="p">;</span>
<span class="k">worker_processes</span> <span class="s">auto</span><span class="p">;</span>
<span class="k">error_log</span> <span class="s">/var/log/nginx/error.log</span><span class="p">;</span>
<span class="k">pid</span> <span class="s">/run/nginx.pid</span><span class="p">;</span>

<span class="c1"># Load dynamic modules. See /usr/share/doc/nginx/README.dynamic.</span>
<span class="k">include</span> <span class="n">/usr/share/nginx/modules/*.conf</span>;

<span class="k">events</span> <span class="p">{</span>
    <span class="kn">worker_connections</span> <span class="mi">1024</span><span class="p">;</span>
<span class="p">}</span>

<span class="k">http</span> <span class="p">{</span>
    <span class="kn">log_format</span>  <span class="s">main</span>  <span class="s">'</span><span class="nv">$remote_addr</span> <span class="s">-</span> <span class="nv">$remote_user</span> <span class="s">[</span><span class="nv">$time_local]</span> <span class="s">"</span><span class="nv">$request"</span> <span class="s">'</span>
                      <span class="s">'</span><span class="nv">$status</span> <span class="nv">$body_bytes_sent</span> <span class="s">"</span><span class="nv">$http_referer"</span> <span class="s">'</span>
                      <span class="s">'"</span><span class="nv">$http_user_agent"</span> <span class="s">"</span><span class="nv">$http_x_forwarded_for"'</span><span class="p">;</span>

    <span class="kn">access_log</span>  <span class="s">/var/log/nginx/access.log</span>  <span class="s">main</span><span class="p">;</span>

    <span class="kn">sendfile</span>            <span class="no">on</span><span class="p">;</span>
    <span class="kn">tcp_nopush</span>          <span class="no">on</span><span class="p">;</span>
    <span class="kn">tcp_nodelay</span>         <span class="no">on</span><span class="p">;</span>
    <span class="kn">keepalive_timeout</span>   <span class="mi">65</span><span class="p">;</span>
    <span class="kn">types_hash_max_size</span> <span class="mi">2048</span><span class="p">;</span>

    <span class="kn">include</span>             <span class="s">/etc/nginx/mime.types</span><span class="p">;</span>
    <span class="kn">default_type</span>        <span class="s">application/octet-stream</span><span class="p">;</span>

    <span class="c1"># Load modular configuration files from the /etc/nginx/conf.d directory.</span>
    <span class="c1"># See http://nginx.org/en/docs/ngx_core_module.html#include</span>
    <span class="c1"># for more information.</span>
    <span class="kn">include</span> <span class="s">/etc/nginx/conf.d/*.conf</span><span class="p">;</span>

    <span class="c1"># 转发websocket需要的设置</span>
    <span class="kn">proxy_set_header</span> <span class="s">X-Real_IP</span> <span class="nv">$remote_addr</span><span class="p">;</span>
    <span class="kn">proxy_set_header</span> <span class="s">Host</span> <span class="nv">$host</span><span class="p">;</span>
    <span class="kn">proxy_set_header</span> <span class="s">X_Forward_For</span> <span class="nv">$proxy_add_x_forwarded_for</span><span class="p">;</span>
    <span class="kn">proxy_http_version</span> <span class="mi">1</span><span class="s">.1</span><span class="p">;</span>
    <span class="kn">proxy_set_header</span> <span class="s">Upgrade</span> <span class="nv">$http_upgrade</span><span class="p">;</span>
    <span class="kn">proxy_set_header</span> <span class="s">Connection</span> <span class="s">'upgrade'</span><span class="p">;</span>

    <span class="c1"># Service1服务配置</span>
    <span class="kn">server</span> <span class="p">{</span>
        <span class="kn">listen</span>       <span class="mi">80</span><span class="p">;</span>
        <span class="kn">server_name</span>  <span class="s">www.service1.com</span><span class="p">;</span>
        <span class="kn">root</span>         <span class="s">/usr/share/nginx/html</span><span class="p">;</span>

        <span class="c1"># Load configuration files for the default server block.</span>
        <span class="kn">include</span> <span class="s">/etc/nginx/default.d/*.conf</span><span class="p">;</span>

        <span class="kn">location</span> <span class="s">/</span> <span class="p">{</span>
            <span class="kn">proxy_pass</span> <span class="s">http://localhost:8188/</span><span class="p">;</span>
            <span class="c1"># 设置是否允许 cookie 传输</span>
            <span class="kn">add_header</span> <span class="s">Access-Control-Allow-Credentials</span> <span class="s">true</span><span class="p">;</span>
            <span class="c1"># 允许请求地址跨域 * 做为通配符</span>
            <span class="kn">add_header</span> <span class="s">Access-Control-Allow-Origin</span> <span class="s">*</span> <span class="s">always</span><span class="p">;</span>
            <span class="c1"># 允许跨域的请求方法</span>
            <span class="kn">add_header</span> <span class="s">Access-Control-Allow-Methods</span> <span class="s">'GET,</span> <span class="s">POST,</span> <span class="s">PUT,</span> <span class="s">DELETE,</span> <span class="s">OPTIONS'</span><span class="p">;</span>
            <span class="kn">add_header</span> <span class="s">Access-Control-Allow-Headers</span> <span class="s">'DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization'</span><span class="p">;</span>

            <span class="kn">if</span> <span class="s">(</span><span class="nv">$request_method</span> <span class="p">=</span> <span class="s">'OPTIONS')</span> <span class="p">{</span>
                <span class="kn">return</span> <span class="mi">204</span><span class="p">;</span>
            <span class="p">}</span>
        <span class="p">}</span>

        <span class="kn">error_page</span> <span class="mi">404</span> <span class="s">/404.html</span><span class="p">;</span>
            <span class="kn">location</span> <span class="p">=</span> <span class="s">/40x.html</span> <span class="p">{</span>
        <span class="p">}</span>

        <span class="kn">error_page</span> <span class="mi">500</span> <span class="mi">502</span> <span class="mi">503</span> <span class="mi">504</span> <span class="s">/50x.html</span><span class="p">;</span>
            <span class="kn">location</span> <span class="p">=</span> <span class="s">/50x.html</span> <span class="p">{</span>
        <span class="p">}</span>
    <span class="p">}</span>

    <span class="c1"># Service2的配置</span>
    <span class="kn">server</span> <span class="p">{</span>
        <span class="kn">listen</span>       <span class="mi">80</span><span class="p">;</span>
        <span class="kn">server_name</span>  <span class="s">www.service2.com</span><span class="p">;</span>
        <span class="kn">root</span>         <span class="s">/usr/share/nginx/html</span><span class="p">;</span>

        <span class="c1"># Load configuration files for the default server block.</span>
        <span class="kn">include</span> <span class="s">/etc/nginx/default.d/*.conf</span><span class="p">;</span>

        <span class="kn">location</span> <span class="s">/</span> <span class="p">{</span>
            <span class="kn">proxy_pass</span> <span class="s">http://localhost:9096/</span><span class="p">;</span>
            <span class="kn">add_header</span> <span class="s">Access-Control-Allow-Origin</span> <span class="s">*</span> <span class="s">always</span><span class="p">;</span>
            <span class="kn">add_header</span> <span class="s">'Access-Control-Allow-Methods'</span> <span class="s">'GET,POST,OPTIONS,PUT,DELETE'</span> <span class="s">always</span><span class="p">;</span>
            <span class="kn">add_header</span> <span class="s">Access-Control-Allow-Headers</span> <span class="s">'DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization'</span><span class="p">;</span>

            <span class="kn">if</span> <span class="s">(</span><span class="nv">$request_method</span> <span class="p">=</span> <span class="s">'OPTIONS')</span> <span class="p">{</span>
                <span class="kn">return</span> <span class="mi">204</span><span class="p">;</span>
            <span class="p">}</span>
        <span class="p">}</span>

        <span class="kn">error_page</span> <span class="mi">404</span> <span class="s">/404.html</span><span class="p">;</span>
            <span class="kn">location</span> <span class="p">=</span> <span class="s">/40x.html</span> <span class="p">{</span>
        <span class="p">}</span>

        <span class="kn">error_page</span> <span class="mi">500</span> <span class="mi">502</span> <span class="mi">503</span> <span class="mi">504</span> <span class="s">/50x.html</span><span class="p">;</span>
            <span class="kn">location</span> <span class="p">=</span> <span class="s">/50x.html</span> <span class="p">{</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre>
     </div>
     <h3 id="_5">
      浏览器访问确认
     </h3>
     <p>
      F12打开调试工具，在请求中，会开到响应头和请求头都会有下面的字段：
     </p>
     <p>
      请求头：
     </p>
     <div class="codehilite">
      <pre><span></span><code><span class="err">GET /route/xxxxxx</span>
<span class="err">Host: host</span>
<span class="err">Connection: keep-alive</span>
<span class="err">Access-Control-Allow-Origin: *</span>
<span class="err">Accept: application/json, text/plain, */*</span>
<span class="err">Authorization: xxxxxxxxxx</span>
<span class="err">User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.85 Safari/537.36</span>
<span class="err">Origin: http://xxx.com</span>
<span class="err">Referer: http://xxx.com</span>
<span class="err">Accept-Encoding: gzip, deflate</span>
<span class="err">Accept-Language: zh-CN,zh;q=0.9</span>
</code></pre>
     </div>
     <p>
      相应头：
     </p>
     <div class="codehilite">
      <pre><span></span><code><span class="err">HTTP/1.1 200 OK</span>
<span class="c">Server: nginx/1.14.1</span>
<span class="c">Date: Sat, 24 Apr 2021 15:17:10 GMT</span>
<span class="nt">Content-Type:</span><span class="w"> </span><span class="nl">application</span><span class="dl">/</span><span class="nl">json</span>;<span class="w"> </span><span class="na">charset</span><span class="o">=</span><span class="s">utf-8</span><span class="w"></span>
<span class="c">Transfer-Encoding: chunked</span>
<span class="c">Connection: keep-alive</span>
<span class="c">Access-Control-Allow-Origin: *</span>
<span class="c">Access-Control-Allow-Methods: GET,POST,OPTIONS,PUT,DELETE</span>
<span class="c">Access-Control-Allow-Headers: DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization</span>
</code></pre>
     </div>
     <h2 id="_6">
      总结
     </h2>
     <p>
      本篇介绍了如果通过nginx网关配置去解决跨域问题，需要在请求中添加相关的头即可
     </p>
     <p>
      最后再说明排查的第一步，看看出现跨域问题时，返回的响应头是否符合规范，重点是下面两个，其他的也需要关注，但目前遇到的问题，基本都是下面两个返回不规范导致的：
     </p>
     <ul>
      <li>
       Access-Control-Allow-Origin: *
      </li>
      <li>
       Access-Control-Allow-Methods: GET,POST,OPTIONS,PUT,DELETE
      </li>
     </ul>
    </article>
   </div>
   <!-- Footer -->
   <footer id="footer">
    <p class="copyright">
     © Untitled. Design:
     <a href="https://html5up.net">
      HTML5 UP
     </a>
     .
    </p>
   </footer>
  </div>
  <!-- BG -->
  <div id="bg">
  </div>
  <!-- Scripts -->
  <script src="../assets/js/jquery.min.js">
  </script>
  <script src="../assets/js/browser.min.js">
  </script>
  <script src="../assets/js/breakpoints.min.js">
  </script>
  <script src="../assets/js/util.js">
  </script>
  <script src="../assets/js/main.js">
  </script>
 </body>
</html>
